package com.yhao.stocknew.common.service;

import com.gargoylesoftware.htmlunit.*;

import com.yhao.stocknew.common.listener.MyJSErrorListener;
import com.yhao.stocknew.common.utils.DateUtil;
import com.yhao.stocknew.entity.DayInfo;
import com.yhao.stocknew.entity.HisInfo;
import com.yhao.stocknew.entity.PlateInfo;
import com.yhao.stocknew.entity.StockInfo;
import com.yhao.stocknew.service.DayInfoService;
import com.yhao.stocknew.service.HisInfoService;
import com.yhao.stocknew.service.PlateInfoService;
import com.yhao.stocknew.service.StockInfoService;
import lombok.extern.slf4j.Slf4j;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.math.BigDecimal;
import java.net.URL;
import java.time.LocalDateTime;
import java.util.*;

/**
 * @author yhao
 * @date 2022-04-13 23:45
 * @desc 同花顺爬虫
 */
@Service
@Slf4j
public class THSService {

    WebClient webClient = null;

    //初始化
    public THSService() {
        webClient = new WebClient(BrowserVersion.CHROME);//新建一个模拟谷歌Chrome浏览器的浏览器客户端对象
        webClient.getOptions().setThrowExceptionOnScriptError(false);//当JS执行出错的时候是否抛出异常, 这里选择不需要
        webClient.getOptions().setThrowExceptionOnFailingStatusCode(false);//当HTTP的状态非200时是否抛出异常, 这里选择不需要
        webClient.getOptions().setActiveXNative(false);
        webClient.getOptions().setCssEnabled(false);//是否启用CSS, 因为不需要展现页面, 所以不需要启用
        webClient.getOptions().setJavaScriptEnabled(true); //很重要，启用JS。有些网站要开启！
        webClient.setAjaxController(new NicelyResynchronizingAjaxController());//很重要，设置支持AJAX
        webClient.getOptions().setTimeout(30000);
        // javascript 异常处理
        webClient.setJavaScriptErrorListener(new MyJSErrorListener());
        webClient.setJavaScriptTimeout(10000);
    }

    //获取某个url的web客户端
    public String htmlUnitUrl(String url, WebClient webClient) {
        try {
            WebRequest request = new WebRequest(new URL(url), HttpMethod.GET);
            Map<String, String> additionalHeaders = new HashMap<String, String>();
            additionalHeaders
                    .put("User-Agent",
                            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.60 Safari/537.36");
            additionalHeaders.put("Accept-Language", "zh-CN,zh;q=0.8");
            additionalHeaders.put("Accept", "*/*");
            additionalHeaders.put("Cookie", "__bid_n=18753bc5a8eb31c0a24207; searchGuide=sg; " +
                    "FPTOKEN=6umg3gP83OIvTNbeSy9Byi/+JmBA8uBWlIDREytNcxeII/ODu+dTV+qJ/+vflpMlPzCaKJyk8URwNLp00pRBfYwMX0ycQiJ3kaI31TQNXFG0j+cSNCOV2bx9sphPKEwmwqjG0kgI3ifBO0upmfLVkyACizlajcMzJEauu6NatjVT6dAQvhrxwQTo9ExwwYXIEfin3b6OsyGW2OvaA4X2i+oyFsfVWprxaqvLmEXVfT8LUfn62ajvDan5MkwYS+PqByWGC/F7ayN4O6ahf2HEBsHP0TwbyPc5Pr6ocSDOYy/TX62uS0gJ2J1bOdbCrBMY9ltXQniQZ/N4utcvIUseKzK2bGX9iHRdVZBiCwfSXvXn8uXQQUT7OahoPkHXEkkSEP+7XffB5MbOSEO+L2s6QA==|mjrsxn3r5LImSzj3vBY8UsMWoGOBZng7MYbyWIP6PIg=|10|a6664b9948c2a24004a29d80cd3d8487; __utma=156575163.1519783955.1699830546.1699830546.1699830546.1; __utmz=156575163.1699830546.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); _ga=GA1.1.910357632.1699830850; _ga_KQBDS1VPQF=GS1.1.1699830850.1.1.1699831393.0.0.0; spversion=20130314; Hm_lvt_722143063e4892925903024537075d0d=1699830463,1699830722,1701473874,1701499815; Hm_lvt_929f8b362150b1f77b477230541dbbc2=1699830463,1699830722,1701473874,1701499815; Hm_lvt_78c58f01938e4d85eaf619eae71b4ed1=1701305844,1701389108,1701473874,1701499815; cmsad_170_0=0; cmsad_171_0=0; cmsad_172_0=0; Hm_lvt_22a3c65fd214b0d5fd3a923be29458c7=1701303286,1701305844,1701389108,1701499842; Hm_lvt_f79b64788a4e377c608617fba4c736e2=1701311371,1701389114,1701500030; Hm_lpvt_f79b64788a4e377c608617fba4c736e2=1701500030; log=; Hm_lpvt_722143063e4892925903024537075d0d=1701503416; Hm_lpvt_929f8b362150b1f77b477230541dbbc2=1701503416; Hm_lpvt_78c58f01938e4d85eaf619eae71b4ed1=1701506131; Hm_lpvt_22a3c65fd214b0d5fd3a923be29458c7=1701506131; historystock=603320%7C*%7C300604%7C*%7C300494%7C*%7C836957%7C*%7C835579; v=A24_WHSvhx9wAfNXJv83ubPFv881bzKyRDLmRJg13FUsgwBxAP-CeRTDNuNr");
            request.setAdditionalHeaders(additionalHeaders);
            // 获取某网站页面
            Page page = webClient.getPage(request);
            return page.getWebResponse().getContentAsString();
        } catch (Exception e) {
            log.error(e.getLocalizedMessage());
        } finally {
            webClient.close();
        }
        return null;
    }

    /**
     * @param pageCount
     * @param baseUrl
     * @desc 获取每天的板块信息
     */
    public boolean getDayPlateInfo(int pageCount, String baseUrl) {
        String url = "http://q.10jqka.com.cn/";
        for (int i = 1; i <= pageCount; i++) {
            // if (pageCount % 2 == 1) {
            // 打开一次同花顺的主网站
            try {
                webClient.getPage(url);//打开网页
            } catch (IOException e) {
                // e.printStackTrace();
                log.info("getDayPlateInfo  continue 执行到这里报错了" + DateUtil.getCommonDate());
                continue;
            }
            // 休眠时间
            try {
                Thread.sleep(new Random().nextInt(2000) + 2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // }
            String finalUrl = baseUrl + i + "/ajax/1/";
            String content = htmlUnitUrl(finalUrl, webClient);
            if (content.contains("Nginx forbidden.")) {
                String errMsg = "被搞了啊========>finalUrl=" + finalUrl;
                log.error(errMsg);
                //return;
            } else {
                log.info("正在解析========>finalUrl=" + finalUrl);
                saxAndSavePlateInfo(content);
            }
            // 休眠时间
            try {
                Thread.sleep(new Random().nextInt(2000) + 2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return true;
    }

    @Autowired
    private PlateInfoService plateInfoService;

    /**
     * @param content
     * @desc 解析和保存每天的板块信息
     */
    private boolean saxAndSavePlateInfo(String content) {
        PlateInfo plateInfo = null;
        ArrayList<PlateInfo> list = new ArrayList();
        Document document = Jsoup.parse(content);
        Elements tbody = document.getElementsByTag("tbody");
        Elements trElements = null;
        Elements tdElements = null;
        for (Element bodyElement : tbody) {
            trElements = bodyElement.getElementsByTag("tr");
            for (Element trElement : trElements) {
                tdElements = trElement.getElementsByTag("td");
                plateInfo = new PlateInfo(tdElements.get(1).text(),
                        new BigDecimal(tdElements.get(2).text()),
                        new BigDecimal(tdElements.get(3).text()),
                        new BigDecimal(tdElements.get(4).text()),
                        new BigDecimal(tdElements.get(5).text()),
                        Integer.parseInt(tdElements.get(6).text()),
                        Integer.parseInt(tdElements.get(7).text()),
                        new BigDecimal(tdElements.get(8).text()),
                        tdElements.get(9).text(),
                        new BigDecimal(tdElements.get(10).text()),
                        new BigDecimal(tdElements.get(11).text()),
                        DateUtil.getDate("yyyy-MM-dd"),
                        // "2023-11-13",
                        LocalDateTime.now());
                list.add(plateInfo);
            }
        }
        boolean result = plateInfoService.saveBatch(list);
        log.info("批量保存结果=====>>>" + result);
        return result;
    }

    public void getDayInfo(int pageCount, String baseUrl) {
        for (int i = 1; i <= pageCount; i++) {
            // 每三次调用一次
            if (i % 3 == 1) {
                String url = "http://q.10jqka.com.cn/";
                // 这个值用不到 但是不访问就不行

                try {
                    webClient.getPage(url);//打开网页
                } catch (IOException e) {
                    // e.printStackTrace();
                    continue;
                }
            }
            // 休眠时间
            try {
                Thread.sleep(new Random().nextInt(2000) + 2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            String finalUrl = baseUrl + i + "/ajax/1/";
            String content = htmlUnitUrl(finalUrl, webClient);
            if (content.contains("Nginx forbidden.")) {
                String errMsg = "被搞了啊========>finalUrl=" + finalUrl;
                log.error(errMsg);
                //return;
            } else {
                log.info("正在解析========>finalUrl=" + finalUrl);
                saxAndSaveDayInfo(content);
            }
            // 休眠时间
            try {
                Thread.sleep(new Random().nextInt(2000) + 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    @Autowired
    private DayInfoService dayInfoService;
    @Autowired
    private HisInfoService hisInfoService;

    private void saxAndSaveDayInfo(String content) {
        DayInfo dayInfo = null;
        ArrayList<DayInfo> dayInfos = new ArrayList();
        Document document = Jsoup.parse(content);
        Elements tbody = document.getElementsByTag("tbody");
        Elements trElements = null;
        Elements tdElements = null;

        String dealAmountStr = "";
        BigDecimal dealAmount = null;

        String circulateNumberStr = "";
        BigDecimal circulateNumber = null;

        String marketValueStr = "";
        BigDecimal marketValue = null;

        String peRatioStr = "";
        BigDecimal peRatio = null;
        HisInfo hisInfo = null;
        ArrayList<HisInfo> hisInfos = new ArrayList();
        for (Element bodyElement : tbody) {
            trElements = bodyElement.getElementsByTag("tr");
            for (Element trElement : trElements) {
                tdElements = trElement.getElementsByTag("td");


                if (null == tdElements.get(10).text() || tdElements.get(10).text().contains("--") || "".equals(tdElements.get(10).text().trim())) {
                    dealAmountStr = "0";
                } else {
                    dealAmountStr = tdElements.get(10).text().trim();
                }
                if (dealAmountStr.contains("万")) {
                    dealAmount = new BigDecimal(dealAmountStr.replace("万", "")).divide(new BigDecimal(10000)).setScale(6, BigDecimal.ROUND_HALF_DOWN);
                } else {
                    dealAmount = new BigDecimal(dealAmountStr.replace("亿", ""));
                }

                if (tdElements.get(11).text() == null || "".equals(tdElements.get(11).text().trim()) || tdElements.get(11).text().contains("--")) {
                    circulateNumberStr = "0";
                } else {
                    circulateNumberStr = tdElements.get(11).text().trim();
                }
                if (circulateNumberStr.contains("万")) {
                    circulateNumber = new BigDecimal(circulateNumberStr.replace("万", "")).divide(new BigDecimal(10000)).setScale(6, BigDecimal.ROUND_HALF_DOWN);
                } else {
                    circulateNumber = new BigDecimal(circulateNumberStr.replace("亿", ""));
                }

                if (tdElements.get(12).text() == null || "".equals(tdElements.get(12).text().trim()) || tdElements.get(12).text().contains("--")) {
                    marketValueStr = "0";
                } else {
                    marketValueStr = tdElements.get(12).text().trim();
                }
                if (marketValueStr.contains("万")) {
                    marketValue = new BigDecimal(marketValueStr.replace("万", "")).divide(new BigDecimal(10000)).setScale(6, BigDecimal.ROUND_HALF_DOWN);
                } else {
                    marketValue = new BigDecimal(marketValueStr.replace("亿", ""));
                }

                if (tdElements.get(13).text() == null || "".equals(tdElements.get(13).text().trim()) || tdElements.get(13).text().contains("--") || tdElements.get(13).text().contains("亏损")) {
                    peRatioStr = "0.0";
                } else {
                    peRatioStr = tdElements.get(13).text().trim();
                }
                if (peRatioStr.contains("万")) {
                    peRatio = new BigDecimal(peRatioStr.replace("万", "")).divide(new BigDecimal(10000)).setScale(6, BigDecimal.ROUND_HALF_DOWN);
                } else {
                    peRatio = new BigDecimal(peRatioStr.replace("亿", ""));
                }

                String nowPriceStr = tdElements.get(3).text();
                BigDecimal nowPrice = null;
                if (nowPriceStr == null || "".equals(nowPriceStr.trim()) || nowPriceStr.contains("--")) {
                    nowPrice = new BigDecimal(0);
                } else {
                    nowPrice = new BigDecimal(nowPriceStr);
                }

                BigDecimal rfa = null;
                String rfaStr = tdElements.get(4).text();
                if (rfaStr == null || "".equals(rfaStr.trim()) || rfaStr.contains("--")) {
                    rfa = new BigDecimal(0);
                } else {
                    rfa = new BigDecimal(rfaStr);
                }

                BigDecimal rf = null;
                String rfStr = tdElements.get(5).text();
                if (rfStr == null || "".equals(rfStr.trim()) || rfStr.contains("--")) {
                    rf = new BigDecimal(0);
                } else {
                    rf = new BigDecimal(rfStr);
                }

                BigDecimal rfs = null;
                String rfsStr = tdElements.get(6).text();
                if (rfsStr == null || "".equals(rfsStr.trim()) || rfsStr.contains("--")) {
                    rfs = new BigDecimal(0);
                } else {
                    rfs = new BigDecimal(rfsStr);
                }
                BigDecimal changeHand = null;
                String changeHandStr = tdElements.get(7).text();
                if (changeHandStr == null || "".equals(changeHandStr.trim()) || changeHandStr.contains("--")) {
                    changeHand = new BigDecimal(0);
                } else {
                    changeHand = new BigDecimal(changeHandStr);
                }

                BigDecimal qRatio = null;
                String qRatioStr = tdElements.get(8).text();
                if (qRatioStr == null || "".equals(qRatioStr.trim()) || qRatioStr.contains("--")) {
                    qRatio = new BigDecimal(0);
                } else {
                    qRatio = new BigDecimal(qRatioStr);
                }
                // BigDecimal amplitude
                BigDecimal amplitude = null;
                String amplitudeStr = tdElements.get(9).text();
                if (amplitudeStr == null || "".equals(amplitudeStr.trim()) || amplitudeStr.contains("--")) {
                    amplitude = new BigDecimal(0);
                } else {
                    amplitude = new BigDecimal(amplitudeStr);
                }
                dayInfo = new DayInfo(tdElements.get(1).text(),
                        tdElements.get(2).text(),
                        nowPrice,
                        rfa,
                        rf,
                        rfs,
                        changeHand,
                        qRatio,
                        amplitude,
                        dealAmount,
                        circulateNumber,
                        marketValue,
                        peRatio,
                        DateUtil.getDate("yyyy-MM-dd"),
                        LocalDateTime.now());
                dayInfos.add(dayInfo);

                hisInfo = new HisInfo(tdElements.get(1).text(),
                        tdElements.get(2).text(),
                        nowPrice,
                        rfa,
                        rf,
                        rfs,
                        changeHand,
                        qRatio,
                        amplitude,
                        dealAmount,
                        circulateNumber,
                        marketValue,
                        peRatio,
                        DateUtil.getDate("yyyy-MM-dd"),
                        LocalDateTime.now());
                hisInfos.add(hisInfo);
            }
        }
        dayInfoService.saveBatch(dayInfos);
        hisInfoService.saveBatch(hisInfos);
    }

    @Autowired
    private StockInfoService stockInfoService;

    /**
     * @param pageCount
     * @return
     * @desc 获取全部股票基础信息
     */
    public String getAllStockBaseInfo(int pageCount, String baseUrl) {

        for (int i = 1; i <= pageCount; i++) {
            // 每三次调用一次
            if (i % 3 == 1) {
                String url = "http://q.10jqka.com.cn/";
                // 这个值用不到 但是不访问就不行

                try {
                    webClient.getPage(url);//打开网页
                } catch (IOException e) {
                    // e.printStackTrace();
                    continue;
                }

                // 休眠时间
                try {
                    Thread.sleep(new Random().nextInt(2000) + 2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            String finalUrl = baseUrl + i + "/ajax/1/";
            String content = htmlUnitUrl(finalUrl, webClient);
            if (content.contains("Nginx forbidden.")) {
                String errMsg = "被搞了啊========>finalUrl=" + finalUrl;
                log.error(errMsg);
                //return;
            } else {
                log.info("正在解析========>finalUrl=" + finalUrl);
                saxAndSaveStockInfo(content);
            }
            // 休眠时间
            try {
                Thread.sleep(new Random().nextInt(2000) + 2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        log.info("全部生成完成------------------->>" + DateUtil.getDate());
        return null;
    }


    private void saxAndSaveStockInfo(String content) {
        StockInfo stockInfo = null;
        ArrayList<StockInfo> stockInfos = new ArrayList<>();
        Document document = Jsoup.parse(content);
        Elements tbody = document.getElementsByTag("tbody");
        Elements trElements = null;
        Elements tdElements = null;
        String dCode = "";
        String dName = "";
        for (Element bodyElement : tbody) {
            trElements = bodyElement.getElementsByTag("tr");
            for (Element trElement : trElements) {
                tdElements = trElement.getElementsByTag("td");
                if (tdElements.size() > 14) {
                    dCode = tdElements.get(1).text();
                    dName = tdElements.get(2).text();
                    stockInfo = new StockInfo(dCode, dName);
                    stockInfos.add(stockInfo);
                }
            }
        }
        final boolean saveBatchFlag = stockInfoService.saveBatch(stockInfos);
        log.info("保存结果为：" + saveBatchFlag);
    }
}
